﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Cosmos.Core.Plugs;
using Cosmos.IL2CPU.X86.IL;
using DimOS.Constants;
using DimOS.DimSH;
using DimOS.Internals;
using DimOS.Plugs;
using DateTime = DimOS.Plugs.DateTime;
using String = DimOS.Plugs.String;
using UI = DimOS.DimSH.Interface;
using TERM = DimOS.DimSH.Terminal;

namespace DimOS.DimFS
{
    public static class Layout
    {
        public static Node RootNode = Nodes.List[0];

        public static string Data = Path.Get(Nodes.List[1], false);

        public static string Temp = Path.Get(Nodes.List[2], false);

        public static string User = Path.Get(Nodes.List[3], false);

        public static string Conf = Path.Get(Nodes.List[4], false);

        public static string StringStub = "";

        public static List<Node> NodeStub = new List<Node>();

        public static List<string> ListStub = new List<string>(); 

        public static void Initialize()
        {
            try
            { 
                Nodes.List[0].Content.Children.Add(Nodes.List[1]);
                Nodes.List[0].Content.Children.Add(Nodes.List[2]);
                Nodes.List[0].Content.Children.Add(Nodes.List[3]);
                Nodes.List[0].Content.Children.Add(Nodes.List[4]);

                Nodes.List[1].Content.Children.Add(Nodes.List[6]);
                Nodes.List[1].Content.Children.Add(Nodes.List[7]);

                Nodes.List[3].Content.Children.Add(Nodes.List[5]);
                Nodes.List[3].Content.Children.Add(Nodes.List[8]);
                Nodes.List[3].Content.Children.Add(Nodes.List[9]);

                Nodes.List[4].Content.Children.Add(Nodes.List[10]);

                Nodes.List[1].Parent = Nodes.List[0];
                Nodes.List[2].Parent = Nodes.List[0];
                Nodes.List[3].Parent = Nodes.List[0];
                Nodes.List[4].Parent = Nodes.List[0];

                Nodes.List[5].Parent = Nodes.List[3];

                Nodes.List[6].Parent = Nodes.List[1];
                Nodes.List[7].Parent = Nodes.List[1];

                Nodes.List[8].Parent = Nodes.List[3];
                Nodes.List[9].Parent = Nodes.List[3];

                Nodes.List[10].Parent = Nodes.List[4];
            }
            catch { UI.InitIndicatorStatusFailed(); }

            var color = UI.Foreground;
            UI.DrawTree(RootNode, true);
            UI.Foreground = color;
        }

        //?
        public static void ListAll(int startIndex, Node node, ref List<NodeLevel> result, bool includeHidden = true)
        {
            var matches = node.FindChildren(includeHidden);
            if (matches.Count != 0)
            {
                for (int j = 0; j < matches.Count; j++) result.Add(new NodeLevel(startIndex, matches[j]));
                for (int i = 0; i < matches.Count; i++) ListAll(startIndex + 1, matches[i], ref result, includeHidden);
            }
        }

        public static List<string> List(string path, Account account, out Error error,
            ref List<string> nonTraversable, ref List<string> insufficientPrivileges,
            ref List<string> notCompleted, ref List<Node> nodes, bool includeHidden = true)
        {
            bool delimited;
            var resolved = Path.Resolve(path, new Permissions(account, true, false, false, false),
                out error, ref nonTraversable, ref insufficientPrivileges, ref notCompleted, ref
                ListStub, out StringStub, out delimited, false, false, false, includeHidden);
            StringStub = ""; ListStub.Clear();
            var result = new List<string>();

            if (resolved.Count == 0) { return result; }

            if (delimited)
                for (int i = 0; i < resolved.Count; i++)
                    for (int j = 0; j < resolved[i].Content.Children.Count; j++)
                    {
                        nodes.Add(resolved[i].Content.Children[j]);
                        result.Add(resolved[i].Content.Children[j].Name +
                            (resolved[i].Content.Children[j].Content.Children != null
                            ? Separators.PathDelimiter.ToString() : ""));
                    }
            else
                for (int i = 0; i < resolved.Count; i++)
                    result.Add(resolved[i].Content.Data.Decode());

            return result;
        }

        public static List<string> Create(string path, Account account, out Error error,
            ref List<string> nonTraversable, ref List<string> insufficientPrivileges,
            ref List<string> notCompleted, ref List<Node> nodes, bool overwrite = false, bool includeHidden = true)
        {
            var result = new List<string>();
            bool delimited;
            nodes = Path.Resolve(path, new Permissions(account, false, false, false, false), out error,
                ref nonTraversable,  ref insufficientPrivileges, ref notCompleted, ref result,
                out StringStub, out delimited, true, overwrite, false, includeHidden);
            StringStub = "";
            return result;
        }

        public static List<string> Delete(string path, Account account, out Error error,
            ref List<string> nonTraversable, ref List<string> insufficientPrivileges,
            ref List<string> notCompleted, bool includeHidden = true)
        {
            var result = new List<string>();
            bool delimited;
            var nodes = Path.Resolve(path, new Permissions(account, false, false, false, true), out error,
                ref nonTraversable, ref insufficientPrivileges, ref notCompleted, ref ListStub,
                out StringStub, out delimited, false, false, false, includeHidden);
            StringStub = ""; ListStub.Clear();
            for (int i = 0; i < nodes.Count; i++)
            {
                result.Add(Path.Get(nodes[i], Settings.AdvancedPaths || delimited/*nodes[i].Content.Children != null*/));
                if (!nodes[i].Destroy()) result.RemoveAt(i);
            }
            return result;
        }

        public static List<string> Copy(string pathFrom, string pathTo, Account account, out Error errorFrom, out Error errorTo,
            ref List<string> nonTraversable, ref List<string> insufficientPrivileges, ref List<string> notCompleted,
            ref List<string> created, ref List<Node> nodes, bool overwrite = false, bool includeHidden = true)
        {
            var result = new List<string>();
            string newName;
            bool delimitedFrom;
            bool delimitedTo;

            var nodesTo = Path.Resolve(pathTo, new Permissions(account, false, true, false, false),
                out errorTo, ref nonTraversable, ref insufficientPrivileges, ref notCompleted,
                ref created, out newName, out delimitedTo, true, false, true, includeHidden);
            var nodesFrom = Path.Resolve(pathFrom, new Permissions(account, true, false, false, false),
                out errorFrom, ref nonTraversable, ref insufficientPrivileges, ref notCompleted,
                ref created, out StringStub, out delimitedFrom, true, false, false, includeHidden);
            StringStub = "";
            for (int i = 0; i < nodesFrom.Count; i++)
                for (int j = 0; j < nodesTo.Count; j++)
                {
                    if (nodesTo[j].ParentExists(nodesFrom[i])) break;

                    var exists = nodesTo[j].FindChildren(newName == "" ? nodesFrom[i].Name : newName);
                    if (!(exists.Count != 0 && !overwrite) && !(exists.Count != 0 &&
                        ((account != Account.Root && !exists[0].Permissions.Delete)
                        || exists[0].Attributes.System)))
                    {
                        if (overwrite) exists[0].Destroy();
                        nodes.Add(nodesFrom[i].Clone(account, nodesTo[j], newName, true));
                        result.Add(Path.Get(nodesFrom[i], Settings.AdvancedPaths || delimitedFrom) +
                            Tokens.Space + Separators.PathCopyDelimiter + Tokens.Space +
                            Path.Get(nodesTo[j], false) + (newName == "" ?
                            Separators.PathDelimiter.ToString() :
                            (Separators.PathDelimiter + "" +
                            Separators.RenamedPathLeft +  newName +
                            Separators.RenamedPathRight + (Settings.AdvancedPaths
                            ? Separators.PathDelimiter.ToString() : (delimitedFrom ?
                            Separators.PathDelimiter.ToString() : "")))));
                    }
                }

            return result;
        }

        public static List<string> Move(string pathFrom, string pathTo, Account account, out Error errorFrom, out Error errorTo,
            ref List<string> nonTraversable, ref List<string> insufficientPrivileges, ref List<string> notCompleted,
            ref List<string> created, ref List<Node> nodes, bool overwrite = false, bool includeHidden = true)
        {
            var result = new List<string>();
            string newName;
            bool delimitedFrom;
            bool delimitedTo;

            var nodesTo = Path.Resolve(pathTo, new Permissions(account, false, true, false, false),
                out errorTo, ref nonTraversable, ref insufficientPrivileges, ref notCompleted,
                ref created, out newName, out delimitedTo, true, false, true, includeHidden);
            var nodesFrom = Path.Resolve(pathFrom, new Permissions(account, true, false, newName != "", true),
                out errorFrom, ref nonTraversable, ref insufficientPrivileges, ref notCompleted,
                ref created, out StringStub, out delimitedFrom, true, false, false, includeHidden);
            StringStub = "";
            for (int i = 0; i < nodesFrom.Count; i++)
                for (int j = 0; j < nodesTo.Count; j++)
                {
                    if (nodesTo[j].ParentExists(nodesFrom[i])) break;

                    var exists = nodesTo[j].FindChildren(newName == "" ? nodesFrom[i].Name : newName);
                    if (!(exists.Count != 0 && !overwrite) && !(exists.Count != 0 &&
                        ((account != Account.Root && !exists[0].Permissions.Delete) ||
                        exists[0].Attributes.System)))
                    {
                        if (overwrite) exists[0].Destroy();
                        nodes.Add(nodesFrom[i].Clone(account, nodesTo[j], newName));
                        nodesFrom[i].Destroy();
                        result.Add(Path.Get(nodesFrom[i], Settings.AdvancedPaths || delimitedFrom) +
                            Tokens.Space + Separators.PathMoveDelimiter + Tokens.Space +
                            Path.Get(nodesTo[j], false) + (newName == "" ?
                            Separators.PathDelimiter.ToString() :
                            (Separators.PathDelimiter + "" +
                            Separators.RenamedPathLeft +  newName +
                            Separators.RenamedPathRight + (Settings.AdvancedPaths
                            ? Separators.PathDelimiter.ToString() : (delimitedFrom ?
                            Separators.PathDelimiter.ToString() : "")))));
                    }
                }

            return result;
        }

        public static List<NodeLevel> Open(string path, Account account, out Error error,
            out bool delimited, ref List<string> nonTraversable, ref List<string> insufficientPrivileges,
            ref List<string> notCompleted, ref List<string> created, bool includeHidden = true)
        {
            var result = new List<NodeLevel>();

            var nodes = Path.Resolve(path, new Permissions(account, true, false, false, false),
                out error, ref nonTraversable, ref insufficientPrivileges, ref notCompleted,
                ref created, out StringStub, out delimited, true, false, false, includeHidden);
            StringStub = "";
            for (int i = 0; i < nodes.Count; i++)
                result.Add(new NodeLevel(!nodes[i].Permissions.Write ? 1 : (delimited ? 2 : 0), nodes[i]));
            return result;
        }

        public static List<string> SetPermissions(string path, Account account,
            bool read, bool write, bool change, bool delete, out Error error,
            ref List<string> nonTraversable, ref List<string> insufficientPrivileges,
            ref List<string> notCompleted, bool includeHidden = true)
        {
            var result = new List<string>();
            bool delimited;

            var nodes = Path.Resolve(path, new Permissions(account, false, false, true, false),
                out error, ref nonTraversable, ref insufficientPrivileges, ref notCompleted,
                ref ListStub, out StringStub, out delimited, false, false, false, includeHidden);
            StringStub = ""; ListStub.Clear();
            for (int i = 0; i < nodes.Count; i++)
            {
                nodes[i].Permissions.Read = read;
                nodes[i].Permissions.Write = write;
                nodes[i].Permissions.Change = change;
                nodes[i].Permissions.Delete = delete;
                result.Add(Path.Get(nodes[i], Settings.AdvancedPaths || delimited));
            }
            return result;
        }

        public static List<string> Search(string name, string content, Attributes attributes, int size,
            Condition sizeCondition, DateTime created, Condition createdCondition, DateTime modified,
            Condition modifiedCondition, DateTime accessed, Condition accessedCondition, Account account,
            ref List<Node> nodes, bool includeHidden = true)
        {
            var temp = new List<NodeLevel>();
            var result = new List<string>();
            ListAll(0, RootNode, ref temp, includeHidden);

            for (int i = 0; i < temp.Count; i++)
            {
                var node = temp[i].Node;
                if ((account == Account.Root || node.Parent.Permissions.Read) &&

                    (name == null || String.IndexOf(node.Name, name) != -1) &&
                    
                    (content == null || ((account == Account.Root || node.Permissions.Read) &&
                    String.IndexOf(node.Content.Data.Decode(), content) != -1)) &&
                    
                    (attributes == null || Attributes.Compare(attributes, node.Attributes)) &&

                    (size < 0 || (sizeCondition == Condition.Equal && node.Size == size) ||
                    (sizeCondition == Condition.Greater && node.Size > size) ||
                    (sizeCondition == Condition.Lesser && node.Size < size) ||
                    (sizeCondition == Condition.GreaterOrEqual && node.Size >= size) ||
                    (sizeCondition == Condition.LesserOrEqual && node.Size <= size)) &&
                    
                    (created == null || (createdCondition == Condition.Equal &&                  
                    DateTime.Compare(node.Created, created) == 0) ||
                    (createdCondition == Condition.Greater && DateTime.Compare(node.Created, created) == 1) ||
                    (createdCondition == Condition.Lesser && DateTime.Compare(node.Created, created) == -1) ||
                    (createdCondition == Condition.GreaterOrEqual && DateTime.Compare(node.Created, created) >= 0) ||
                    (createdCondition == Condition.LesserOrEqual && DateTime.Compare(node.Created, created) <= 0)) &&
                    
                    (modified == null || (modifiedCondition == Condition.Equal &&
                    DateTime.Compare(node.Modified, modified) == 0) ||
                    (modifiedCondition == Condition.Greater && DateTime.Compare(node.Modified, modified) == 1) ||
                    (modifiedCondition == Condition.Lesser && DateTime.Compare(node.Modified, modified) == -1) ||
                    (modifiedCondition == Condition.GreaterOrEqual && DateTime.Compare(node.Modified, modified) >= 0) ||
                    (modifiedCondition == Condition.LesserOrEqual && DateTime.Compare(node.Modified, modified) <= 0)) &&

                    (accessed == null || (accessedCondition == Condition.Equal &&
                    DateTime.Compare(node.Accessed, accessed) == 0) ||
                    (accessedCondition == Condition.Greater && DateTime.Compare(node.Accessed, accessed) == 1) ||
                    (accessedCondition == Condition.Lesser && DateTime.Compare(node.Accessed, accessed) == -1) ||
                    (accessedCondition == Condition.GreaterOrEqual && DateTime.Compare(node.Accessed, accessed) >= 0) ||
                    (accessedCondition == Condition.LesserOrEqual && DateTime.Compare(node.Accessed, accessed) <= 0)))
                {
                    nodes.Add(node);
                    result.Add(Path.Get(node, Settings.AdvancedPaths || node.Content.Children != null));
                }  
            }
            return result;
        }
    }
}
